还要注意的是,C并未很好地实现整数。例如,整数是无穷大的数,但是2字节的int类型只能表示65536个整数。因此,不要混淆抽象概念和具体的实现。
二进制浮点数只能精确地表示多个1/2的幂的和。因此,3/4和7/8可以精确地表示为二进制小数,而1/3和2/5却不能。
定义性声明:需要建立存储空间的(如:int a; )声明。
引用性声明:不需建立存储空间的声明(extern a;)。
声明包括定义,但并非所有的声明都是定义。对“int a;” 而言,它既是声明,又是定义。而对“extern a;” 而言,它是声明而不是定义。
强制类型转换的目的之一:抑制警告信息;
在许多情况下,强制类型转换告诉编译器,“是的,我真的想这样做。”这指示编译器,即使有数据损失或其他危险的可能性,也继续执行数据转换。强制类型转换也可以用来澄清模糊的情况。
使用局部变量节约内存空间。然而,更重要地是,使用局部变量能减少程序不同部分不必要的交互,从而减少了程序的bug,同时也遵循了结构化编程的原则。
void* 类型表示未确定类型的指针。C、C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。
类型未确定,即无法确定对应内存空间的长度和解码方案,强制类型转换,即重新确定长度和解码方案。
void* 类型表示未确定类型的指针。C、C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。
类型未确定,即无法确定对应内存空间的长度和解码方案,强制类型转换,即重新确定长度和解码方案。
序列点是一个时间点,在整个表达式全部计算完毕之后或在||、&&、?:或逗号运算符处,或在函数调用之前,此刻尘埃落定,所有的副作用都己确保结束。ANSI/ISOC标准这样描述:
在上一个和下一个序列点之间,一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值。
第二句话比较费解,它说在一个表达式中如果某个对象需要写入,則在同一表达式中对该对象的访问应该只局限于直接用于计算将要写入的值。这条规則有效地限制了只有能确保在修改之前才访问变a的表达式为非法。例如i=i+1合法,而a[i]=i++則非法。
a = b = a = b 它不需要临时变量就可 以交换 a 和 b 的值。
试图在序列点之间两次修改变量a,这是无定义的。
缓冲输入,将若干内容先存储到称为缓冲区(buffer)的临时存储区域,等待适当时机将缓冲区内的内容变得对程序可用,非缓冲输入是不使用缓冲区,直接让输入对程序可用。缓冲输入对于频繁读写有更高的效率,而非缓冲输入是即时交互程序所需要的。
fopen()函数不仅打开一个文件,还创建一个缓冲区(在读写模式下创建两个缓冲区)以及一个包含文件和缓冲区数据的结构。另外,fopen()返回一个指向该结构的指针,以便其他函数知道如何找到该结构。假设把该指针赋给一个指针变量fp,我们说fopen()函数“打开一个流”。如果以文本模式打开该文件,就获得一个文本流,如果以二进制模式打开该文件,就获得一个二进制流。
这个结构通常包含一个指定流中当前位置的文件指示器。除此之外,它还包含错误和文件结尾的指示器、一个指向缓冲区开始处的指针、一个文件标识符和一个计数(统计实际拷贝进缓冲区的字节数)。
EOF,对于文本文件,数据以字符的ASCII码值的形式存放,普通字符的ASCII码的范围是32到127(十进制),EOF的16进制代码为0XFF(十进制为-1),因此可以用EOF作为文件的结束标志。当把数据以二进制的形式存入到文件中时,就有可能会将数字-1存入到文件中,此时继续根据EOF来判断文件是否结束就会出问题,为了解决这个问题,ASCI C就提供了foef函数。
fgetc()等函数当读到文件末尾时会设置EOF。
按存储程序和程序控制的概念,控制器的程序计数器确定指令的读取位置,通常是顺序读取,但也可以跳转地址读取不同的指令。而高级语言的条件、循环语句的控制结构的实质就是跳转读取指令,类似于汇编语言的jmp和Basic语言的goto。
二分的思想也可以用在if结构中,使逻辑结构更清晰。以及正态分布最部的部分最先做判断。
函数要处理的不是指针本身,而是指针所指向的数据。当要处理指针本身时,需要使用双重指针或指针引用。
读写字符可以character by character,也可以line by line,但如果用一个固定大小的数组来表示line读入存储的位置时,始终觉得不是很妥,在C++中,就有了好的解决方案,使用string。当然,C的变长数组可以考虑。
对于传统的C数组,要求size是整型常量表达式,但c99/c11允许使用整型非常量表达式,这种情况下的数组被称为变长数组。
c99的变长数组是指允许使用变量表示数组的维度,但一量创建数组,其大小就能再改变了。所谓的变,只是在创建数组时可以使用变量指定数组的维度,创建以后就能再变了。
某程序员为一个蓝牙芯片(cortex-M3)做软件开发。这个芯片只有几K的内存,这种情况下,默认除了全局变量,其它的内存都会给栈,不会留内存给动态分配。在一些地方用了变长数组,结果测试的时候,经常就会莫名其妙挂掉。后来在翻编译器手册时候,无意看到了变长数组说明,说这个编译器的变长数组是malloc分配的,不在栈上。。。于是写了一个简单的例子去单步验证,发现运行到变长数组那里后,确实调用了malloc,而且malloc从栈底向栈顶方向分配了指定大小的内存。当然,BUG的主要原因还是因为没有检查输入的数据长度。在汽车,医疗电子行业,一切都是静态的。也就是说,没堆只有栈,很苛刻。
变长数组这个翻译很具有误导性,它的实际意思其实是「以变量作为长度的数组」——区别于「以常数作为长度的数组」。英文Variable-Length Array。注意这里的连接号。连接号代表的意思是,Variable不是一个用于修饰Length的形容词「可变的」,而是名词——即变量。为了不引起歧义,以下以VLA称之。了解了正确的含义后,不难理解VLA的目的就是数组的长度是由一个变量指定的,而非常数。
VLA目前在MSVC/GCC上的实现全部都是利用到了alloca() ,在栈上动态申请一块内存(移动栈指针),当函数返回时,栈指针回归外一层函数时的状态,因而alloca() 分配的内存得以释放。
实际工程中不推荐使用VLA。原因如下:若数组长度很大,有造成爆栈的危险。OS给进程的栈的大小是有限的。使用alloca()后会造成部分与栈操作有关的编译器优化失效。因此,若数组长度有可能很大,请直接在堆上分配避免爆栈。若数组长度有个确定的上限值,可直接用那个上限值来申请栈空间。
变长数组一般是在栈上分配,动态数组一般是在堆上分配。
变长数组在函数返回之后声明周期就结束了,而动态数组需要手动free掉。
变长数组使用多了容易栈溢出,而且它在c11里只是可选特性,和标准 C++ 也不兼容,所以不建议过多使用。
而动态数组,则是数组长度可以随需求变化(扩充)。可以随意用realloc在堆上扩大/缩小你开辟的内存空间。
第一维的下标变化最慢,最右边的下标变化最快。想像一个大圈内套了很多小圈。
C中string的操作是逐字符进行的,C++因为有了封装,其颗粒度就增大了,且可以动态变化。
一旦定义了一个结构体类型的变量,系统在分配内存时就会分配一块连续的空间,依次存放它的每一个分量(CPU按字长访问内存,要考虑内存对齐)。这块空间总的名字就是结构体变量的名字。内部成员还有各自的名字(地址)可访问。C遵循的基本的内存布局假设:通过对象的基地址和数据成员的偏移量获取数据成员的地址。
在C++中,结构体是纯数据的定义,如果还涉及到这些数据的操作,则更建议考虑类类型;
结构体和类都是对基本类型的扩展,用于提高编程的颗粒度;
在C中,CPU以字长为单位访问内存块,内存以字节编址,支持位运算。
信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占一个字节中的一个或几个二进制位,常常在一个字节中放几个信息。
如windows编程的窗口属性便可以是几个属性的组合,使用“|”操作。
位运算自然是以位为单位进行运算,与其它运算不同,有类型编码。所以位运算要把其它进制转换为二进制为做运算。
当一个整数的二进制串中只有一个数位为1时,称为掩码。
程序中通常借助掩码对数据按位进行测试。
在C程序中使用宏,实际上是经过了宏定义、宏调用、宏展开三个步骤。宏定义就是使用#define命令定义一个宏标识符所代表的字符串,而宏调用则是在程序中使用宏标识符代替相应的字符串,宏展开是指在开始编译源程序之前,将程序所有的宏调用使用相应的字符串来代替。
使用参数的宏定义时,一般应将宏定义字符串中的参数都用括号括起来,并且,整个字符串部分也要用括号括起来,这样才能保证在任何替代情况下,把宏定义当做一个整体看待,从而得到一个合理的计算结果。否则,宏展开后,因为优先级、结合性的问题,可能会出现意想不到的结果。
标识符是一段连续内存地址的命名,基本数据类型有编译器规定的长度,所以知道需要访问多长的一段内存地址。数组呢,有长度与类型一起确定内存的长度,字符串呢?以null字符\0来标识一段内存的结束位置。
库:头文件(库函数原型,由编译器来做类型检查)+库文件(函数实现,由链接器链接,将代码复制到可执行文件中)。
斐波拉契数列
f1 f2 f1 = f1+f2 f2 = f1+f2 或者: f1 f2 f2 = f1+f2 f1 = f2-f1
stdin 是一个指向FILE 类型对象的指针类型的表达式。
在很多实现中,它其实是一个“常量”表达式。例如在Visual C++中,stdin 是这样定义的:
#define stdin (&_iob[0])
正确的写代码顺序(成对原则):先写最基本的部分:“printf("");”,在每次写这条函数调用语句时首先写出其最基本的必要部分,然后再向其中添加内容,最后完成“printf("你好!");”。
写语句也是一样,先把语句写完整:
switch () { ; }
然后再向其中的各个部分添加内容。
malloc动态内存分配也是如此 char* p = malloc(sizeof(char)*48); //…… free(p);
事实上,二维数组可以看成是一个特殊的一维数组,而在内存中二维数组也是按行首尾 相接线性排列的。
本页共86段,4925个字符,12313 Byte(字节)